package com.zzzy.doc.service.processdoc;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zzzy.common.util.AssertUtil;
import com.zzzy.common.vo.PageResult;
import com.zzzy.doc.common.enums.DocNotifyMessageModuleEnum;
import com.zzzy.doc.common.enums.DocStatusEnum;
import com.zzzy.doc.common.enums.UrgencyEnum;
import com.zzzy.doc.dao.entity.DocFile;
import com.zzzy.doc.dao.entity.DocUserOperateLink;
import com.zzzy.doc.dao.entity.Document;
import com.zzzy.doc.service.DocService;
import com.zzzy.doc.service.DocUserOperateLinkService;
import com.zzzy.doc.vo.document.DocInfoRes;
import com.zzzy.doc.vo.document.process.ProcessDocButtonRes;
import com.zzzy.doc.vo.document.process.ProcessDocOperateReq;
import com.zzzy.doc.vo.document.process.ProcessDocPageReq;
import com.zzzy.doc.vo.document.process.ProcessDocStatisticsRes;
import com.zzzy.framework.common.dict.DictService;
import com.zzzy.framework.common.util.SecurityUtils;
import com.zzzy.framework.common.util.SortFieldUtil;
import com.zzzy.framework.core.mybatis.query.LambdaQueryWrapperE;
import com.zzzy.framework.core.mybatis.query.MPJLambdaWrapperE;
import com.zzzy.framework.dto.LoginUser;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.Objects;

@Service
public class ProcessDocService extends DocService {

    @Resource
    private DocUserOperateLinkService docUserOperateLinkService;
    @Resource
    private DictService dictService;

    public ProcessDocButtonRes getButton(Long id) {
        validateAuthForQuery(id);
        DocInfoRes docInfo = getDocInfo(id);
        boolean isWord = Objects.isNull(docInfo.getPdfFileBlob());
        boolean isArchived = DocStatusEnum.ARCHIVED.getValue().equals(docInfo.getStatus());
        boolean isDistribute = DocStatusEnum.DISTRIBUTE.getValue().equals(docInfo.getStatus());
        boolean isComplete = DocStatusEnum.COMPLETE.getValue().equals(docInfo.getStatus());
        boolean isCancel = DocStatusEnum.CANCEL.getValue().equals(docInfo.getStatus());
        ProcessDocButtonRes buttonRes = new ProcessDocButtonRes();
        buttonRes.setOpenFinalPdf(!isWord);
        if (isDistribute || isComplete || isArchived || isCancel){
            return buttonRes;
        }
        LoginUser loginUser = SecurityUtils.getLoginUser();
        //是否当前可操作人
        boolean isCurrentOperator = docInfo.getOperatorId().equals(loginUser.getId());
        boolean isAudit = DocStatusEnum.AUDIT.getValue().equals(docInfo.getStatus());
        boolean hasAudit = loginUser.hasControllerPermission("audit");
        boolean isSign = DocStatusEnum.SIGN.getValue().equals(docInfo.getStatus());
        boolean hasSign = loginUser.hasControllerPermission("sign");
        boolean isJointSign = DocStatusEnum.JOINT_SIGN.getValue().equals(docInfo.getStatus());
        boolean hasJointSign = loginUser.hasControllerPermission("jointSign");
        boolean isLeaderSign = DocStatusEnum.LEADER_SIGN.getValue().equals(docInfo.getStatus());
        boolean hasLeaderSign = loginUser.hasControllerPermission("leaderSign");
        buttonRes.setEditFile(isCurrentOperator && ((isAudit && hasAudit) || (isSign && hasSign) || (isJointSign && hasJointSign) || (isLeaderSign && hasLeaderSign)));
        buttonRes.setOpenFinalPdf(Objects.nonNull(docInfo.getPdfFileBlob()));
        buttonRes.setAudit(isCurrentOperator && isAudit && hasAudit);
        buttonRes.setSign(isCurrentOperator && isSign && hasSign);
        buttonRes.setJointSign(isCurrentOperator && isJointSign && hasJointSign);
        buttonRes.setLeaderSign(isCurrentOperator && isLeaderSign && hasLeaderSign);
        buttonRes.setBack(isCurrentOperator);
        return buttonRes;
    }

    /**
     * 办理中公文列表
     */
    public PageResult<Document> getProcessingDocPage(ProcessDocPageReq queryReq) {
        LambdaQueryWrapperE<Document> queryWrapperE = new LambdaQueryWrapperE<>();

        queryWrapperE.select(Document::getId, Document::getSubject, Document::getDocNo, Document::getUrgency
                , Document::getSecretLevel, Document::getSendUnit, Document::getDocPrecis, Document::getUserName
                , Document::getStatus, Document::getOperatorName, Document::getEndDate, Document::getPrintDate, Document::getOperatorId, Document::getUpdateTime);
        Long userId = SecurityUtils.getLoginUser().getId();
        queryWrapperE.eq(Document::getOperatorId, userId)
                .neIfPresent(Document::getUserId, userId)
                .likeIfPresent(Document::getSubject, queryReq.getSubject())
                .likeIfPresent(Document::getDocNo, queryReq.getDocNo())
                .eqIfPresent(Document::getUrgency, queryReq.getUrgency())
                .eqIfPresent(Document::getSecretLevel, queryReq.getSecretLevel())
                .likeIfPresent(Document::getSendUnit, queryReq.getSendUnit())
                .likeIfPresent(Document::getDocPrecis, queryReq.getDocPrecis())
                .likeIfPresent(Document::getDocText, queryReq.getDocText())
                .eqIfPresent(Document::getStatus, queryReq.getStatus())
                .likeIfPresent(Document::getUserName, queryReq.getUserName())
                .betweenIfPresent(Document::getEndDate, queryReq.getBeginEndDate()
                        , Objects.nonNull(queryReq.getEndEndDate()) ? queryReq.getEndEndDate().plusDays(1) : null)
                .betweenIfPresent(Document::getPrintDate, queryReq.getBeginPrintDate()
                        , Objects.nonNull(queryReq.getEndPrintDate()) ? queryReq.getEndPrintDate().plusDays(1) : null)
                .betweenIfPresent(Document::getUpdateTime, queryReq.getBeginUpdateTime()
                        , Objects.nonNull(queryReq.getEndUpdateTime()) ? queryReq.getEndUpdateTime().plusDays(1) : null)
                .orderByDesc(Document::getUrgency).orderByDesc(Document::getUpdateTime).orderByDesc(Document::getId);

        PageResult<Document> pageResult = baseMapper.selectPage(queryReq
                , SortFieldUtil.getSortingFields(Document.class, queryReq.getOrderField(), queryReq.getOrderRule()), queryWrapperE);
        Map<Object, String> urgencyMap = dictService.getDictDataMap(UrgencyEnum.class);
        pageResult.getList().forEach(doc -> doc.setUrgencyDescription(urgencyMap.get(doc.getUrgency())));
        return pageResult;
    }
    @Override
    @Transactional
    public void updateMainWord(Long id, byte[] byteArray, Integer revisionCount) {
        AssertUtil.isTrue(getButton(id).isEditFile(), "用户当前无法修改此公文正文");
        super.updateMainWord(id, byteArray, revisionCount);
    }
    @Transactional
    public void updateApprovalWord(Long id, byte[] byteArray, String advice, String signTime) {
        AssertUtil.isTrue(getButton(id).isEditFile(), "用户当前无法修改此公文呈批表");
        if (StringUtils.isNotBlank(advice)){
            docAdviceService.addAdvice(id, "呈批表中意见：" + advice);
        }
        updateApprovalWord(id, byteArray);
        if (StringUtils.isNotBlank(signTime)){
            Document document = getDocById(id);
            DocFile docFile = docFileService.getDocFileByDocId(id);
            if (DocStatusEnum.JOINT_SIGN.getValue().equals(document.getStatus())){
                docFile.setAddJointSignTime(signTime);
            } else if (DocStatusEnum.LEADER_SIGN.getValue().equals(document.getStatus())){
                docFile.setAddLeaderSignTime(signTime);
            } else {
                return;
            }
            docFileService.updateById(docFile);
        }
    }
    @Override
    @Transactional
    public void updateFinalPdf(Long id, byte[] byteArray) {
        Document document = getDocByIdForQuery(id);
        Long loginUserId = SecurityUtils.getLoginUser().getId();
        AssertUtil.isTrue(loginUserId.equals(document.getOperatorId()) && DocStatusEnum.SIGN.getValue().equals(document.getStatus())
                , "用户当前无法签证此公文正文");
        docFileService.updateFinalPdfByDocId(id, byteArray);

        document.setUpdateTime(LocalDateTime.now());
        baseMapper.updateById(document);
    }

    @Transactional
    public void back(ProcessDocOperateReq operateReq) {
        Document document = getDocByIdForOperate(operateReq.getId());
        String adviceMessage = StringUtils.isBlank(operateReq.getMessage()) ? "驳回" : "驳回，意见：" + operateReq.getMessage();
        docAdviceService.addAdvice(operateReq.getId(), adviceMessage);
        operateDocByDocId(operateReq.getId());

        String notifyMessage = document.getSubject() + " 被驳回";
        docNotifyMessageService.addNotifyMessage(operateReq.getId(), document.getUserId(), DocNotifyMessageModuleEnum.MY_DOC, notifyMessage);
    }

    @Transactional
    public void audit(ProcessDocOperateReq operateReq) {
        Document document = getDocByIdForOperate(operateReq.getId());
        String adviceMessage = StringUtils.isBlank(operateReq.getMessage()) ? "审批修订通过" : "审批修订通过，意见：" + operateReq.getMessage();
        docAdviceService.addAdvice(operateReq.getId(), adviceMessage);
        operateDocByDocId(operateReq.getId());
        String notifyMessage = document.getSubject() + " 审批修订通过";
        docNotifyMessageService.addNotifyMessage(operateReq.getId(), document.getUserId(), DocNotifyMessageModuleEnum.MY_DOC, notifyMessage);
    }

    @Transactional
    public void jointSign(ProcessDocOperateReq operateReq) {
        Document document = getDocByIdForOperate(operateReq.getId());
        String adviceMessage = StringUtils.isBlank(operateReq.getMessage()) ? "会签通过" : "会签通过，意见：" + operateReq.getMessage();
        docAdviceService.addAdvice(operateReq.getId(), adviceMessage);
        operateDocByDocId(operateReq.getId());

        String notifyMessage = document.getSubject() + " 会签通过";
        docNotifyMessageService.addNotifyMessage(operateReq.getId(), document.getUserId(), DocNotifyMessageModuleEnum.MY_DOC, notifyMessage);
    }

    @Transactional
    public void leaderSign(ProcessDocOperateReq operateReq) {
        Document document = getDocByIdForOperate(operateReq.getId());
        String adviceMessage = StringUtils.isBlank(operateReq.getMessage()) ? "领导签发通过" : "领导签发通过，意见：" + operateReq.getMessage();
        docAdviceService.addAdvice(operateReq.getId(), adviceMessage);
        operateDocByDocId(operateReq.getId());

        String notifyMessage = document.getSubject() + " 领导签发通过";
        docNotifyMessageService.addNotifyMessage(operateReq.getId(), document.getUserId(), DocNotifyMessageModuleEnum.MY_DOC, notifyMessage);
    }

    @Transactional
    public void sign(ProcessDocOperateReq operateReq) {
        Document document = getDocByIdForOperate(operateReq.getId());
        String adviceMessage = StringUtils.isBlank(operateReq.getMessage()) ? "签章完毕" : "签章完毕，意见：" + operateReq.getMessage();
        docAdviceService.addAdvice(operateReq.getId(), adviceMessage);
        operateDocByDocId(operateReq.getId());

        String notifyMessage = document.getSubject() + " 签章完毕";
        docNotifyMessageService.addNotifyMessage(operateReq.getId(), document.getUserId(),DocNotifyMessageModuleEnum.MY_DOC, notifyMessage);
    }

    private void operateDocByDocId(Long id) {
        Document document = getDocByIdForQuery(id);
        document.setUpdateTime(LocalDateTime.now());
        document.setOperatorId(document.getUserId());
        document.setOperatorName(document.getUserName());
        baseMapper.updateById(document);

        docUserOperateLinkService.addByDocId(id);
    }

    /**
     * 已办理公文列表
     */
    public PageResult<Document> getProcessedDocPage(ProcessDocPageReq queryReq) {
        MPJLambdaWrapperE<Document> queryWrapperE = new MPJLambdaWrapperE<>();
        queryWrapperE.select(Document::getId, Document::getSubject, Document::getDocNo, Document::getUrgency
                , Document::getSecretLevel, Document::getSendUnit, Document::getDocPrecis, Document::getUserName
                , Document::getStatus, Document::getOperatorName, Document::getEndDate, Document::getPrintDate, Document::getOperatorId, Document::getUpdateTime);

        Long userId = SecurityUtils.getLoginUser().getId();
        queryWrapperE.innerJoin(DocUserOperateLink.class, DocUserOperateLink::getDocId, Document::getId)
                .eq(DocUserOperateLink::getUserId, userId);

        queryWrapperE
                .neIfPresent(Document::getUserId, userId)
                .likeIfPresent(Document::getSubject, queryReq.getSubject())
                .likeIfPresent(Document::getDocNo, queryReq.getDocNo())
                .eqIfPresent(Document::getUrgency, queryReq.getUrgency())
                .eqIfPresent(Document::getSecretLevel, queryReq.getSecretLevel())
                .likeIfPresent(Document::getSendUnit, queryReq.getSendUnit())
                .likeIfPresent(Document::getDocPrecis, queryReq.getDocPrecis())
                .likeIfPresent(Document::getDocText, queryReq.getDocText())
                .eqIfPresent(Document::getStatus, queryReq.getStatus())
                .likeIfPresent(Document::getUserName, queryReq.getUserName())
                .betweenIfPresent(Document::getEndDate, queryReq.getBeginEndDate()
                        , Objects.nonNull(queryReq.getEndEndDate()) ? queryReq.getEndEndDate().plusDays(1) : null)
                .betweenIfPresent(Document::getPrintDate, queryReq.getBeginPrintDate()
                        , Objects.nonNull(queryReq.getEndPrintDate()) ? queryReq.getEndPrintDate().plusDays(1) : null)
                .betweenIfPresent(Document::getUpdateTime, queryReq.getBeginUpdateTime()
                        , Objects.nonNull(queryReq.getEndUpdateTime()) ? queryReq.getEndUpdateTime().plusDays(1) : null)
                .orderByDesc(Document::getUrgency).orderByDesc(Document::getId);
        PageResult<Document> pageResult = baseMapper.selectPage(queryReq
                , SortFieldUtil.getSortingFields(Document.class, queryReq.getOrderField(), queryReq.getOrderRule()), queryWrapperE);
        Map<Object, String> urgencyMap = dictService.getDictDataMap(UrgencyEnum.class);
        pageResult.getList().forEach(doc -> doc.setUrgencyDescription(urgencyMap.get(doc.getUrgency())));
        return pageResult;
    }

    public ProcessDocStatisticsRes getStatistics() {
        Long userId = SecurityUtils.getLoginUser().getId();
        long processedCount = baseMapper.selectProcessedCount(userId);
        Long processing = baseMapper.selectCount(new LambdaQueryWrapper<Document>().eq(Document::getOperatorId, userId).ne(Document::getUserId, userId));
        ProcessDocStatisticsRes statisticsRes = new ProcessDocStatisticsRes();
        statisticsRes.setProcessingCount(processing);
        statisticsRes.setProcessedCount(processedCount);
        return statisticsRes;
    }
}
